package com.wiscamp.ninechapters.common.statistics;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 求统计值帮助类
 */
public class StatisticsHelper {
    private StatisticsHelper() {
    }

    /**
     * 求和
     *
     * @param array
     * @return
     */
    public static double getSum(double[] array) {
        return Arrays.stream(array).summaryStatistics().getSum();
    }

    /**
     * 求均值
     *
     * @param array
     * @return
     */
    public static double getMean(double[] array) {
        return Arrays.stream(array).summaryStatistics().getAverage();
    }

    /**
     * 求众数
     *
     * @param array
     * @return
     */
    public static double getMode(double[] array) {
        Map<Double, Integer> map = new HashMap<>();
        for (int i = 0; i < array.length; i++) {
            if (map.containsKey(array[i])) {
                map.put(array[i], map.get(array[i]) + 1);
            } else {
                map.put(array[i], 1);
            }
        }
        int maxCount = 0;
        double mode = -1;
        Iterator<Double> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            double num = iterator.next();
            int count = map.get(num);
            if (count > maxCount) {
                maxCount = count;
                mode = num;
            }
        }
        return mode;
    }

    /**
     * 求中位数
     *
     * @param array
     * @return
     */
    public static double getMedian(double[] array) {
        double[] tempArr = Arrays.copyOf(array, array.length);
        Arrays.sort(tempArr);
        if (tempArr.length % 2 == 0) {
            return (tempArr[tempArr.length >> 1] + tempArr[(tempArr.length >> 1) - 1]) / 2;
        } else {
            return tempArr[(tempArr.length >> 1)];
        }
    }


    /**
     * 求中列数
     *
     * @param array
     * @return
     */
    public static double getMidrange(double[] array) {
        double max = array[0];
        double min = array[0];
        for (int i = 0; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
            if (array[i] < min) {
                min = array[i];
            }
        }
        return (min + max) / 2;
    }

    /**
     * 求四分位数
     *
     * @param array
     * @return 存放三个四分位数的数组
     */
    public static double[] getQuartiles(double[] array) {
        double[] tempArr = Arrays.copyOf(array, array.length);
        Arrays.sort(tempArr);
        double[] quartiles = new double[3];
        // 第二四分位数（中位数）
        quartiles[1] = getMedian(tempArr);
        // 求另外两个四分位数
        if (tempArr.length % 2 == 0) {
            quartiles[0] = getMedian(Arrays.copyOfRange(tempArr, 0, tempArr.length / 2));
            quartiles[2] = getMedian(Arrays.copyOfRange(tempArr, tempArr.length / 2, tempArr.length));
        } else {
            quartiles[0] = getMedian(Arrays.copyOfRange(tempArr, 0, tempArr.length / 2));
            quartiles[2] = getMedian(Arrays.copyOfRange(tempArr, tempArr.length / 2 + 1, tempArr.length));
        }
        return quartiles;
    }

    /**
     * 求极差
     *
     * @param array
     * @return
     */
    public static double getRange(double[] array) {
        double max = array[0];
        double min = array[0];
        for (int i = 0; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
            if (array[i] < min) {
                min = array[i];
            }
        }
        return max - min;
    }

    /**
     * 求四分位数极差
     *
     * @param array
     * @return
     */
    public static double getQuartilesRange(double[] array) {
        return getRange(getQuartiles(array));
    }

    /**
     * 求截断均值
     *
     * @param array 求值数组
     * @param p     截断量p，例如p的值为20，则截断20%（高10%，低10%）
     * @return
     */
    public static double getTrimmedMean(double[] array, int p) {
        int tmp = array.length * p / 100;
        double[] tempArr = Arrays.copyOfRange(array, tmp, array.length + 1 - tmp);
        return getMean(tempArr);
    }

    /**
     * 求方差
     *
     * @param array
     * @return
     */
    public static double getVariance(double[] array) {
        double variance = 0;
        double sum = 0;
        double sum2 = 0;
        for (int i = 0; i < array.length; i++) {
            sum += array[i];
            sum2 += array[i] * array[i];
        }
        variance = sum2 / array.length - (sum / array.length) * (sum / array.length);
        return variance;
    }

    /**
     * 求绝对平均偏差(AAD)
     *
     * @param array
     * @return
     */
    public static double getAbsoluteAverageDeviation(double[] array) {
        double sum = 0;
        double mean = getMean(array);
        for (int i = 0; i < array.length; i++) {
            sum += Math.abs(array[i] - mean);
        }
        return sum / array.length;
    }

    /**
     * 求中位数绝对偏差(MAD)
     *
     * @param array
     * @return
     */
    public static double getMedianAbsoluteDeviation(double[] array) {
        double[] tempArr = new double[array.length];
        double median = getMedian(array);
        for (int i = 0; i < array.length; i++) {
            tempArr[i] = Math.abs(array[i] - median);
        }
        return getMedian(tempArr);
    }

    /**
     * 求标准差
     *
     * @param array
     * @return
     */
    public static double getStandardDevition(double[] array) {
        double d = getVariance(array);
        return Math.sqrt(d);
    }
}
